package com.seguriapp.common.gwt.client.searchfilter;

import java.util.Date;
import java.util.List;

import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.FocusEvent;
import com.google.gwt.event.dom.client.FocusHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.geolocation.client.Geolocation;
import com.google.gwt.geolocation.client.Position;
import com.google.gwt.geolocation.client.PositionError;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.SmallMapControl;
import com.google.gwt.maps.client.event.MapClickHandler;
import com.google.gwt.maps.client.geocode.Geocoder;
import com.google.gwt.maps.client.geocode.LatLngCallback;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.Size;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.Overlay;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RadioButton;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.datepicker.client.DatePicker;
import com.seguriapp.common.gwt.client.UtilClientValidations;
import com.seguriapp.common.gwt.client.searchfilter.SearchFilterPresenter.Display;
import com.seguriapp.entities.SeguriAppEntity;

public class SearchFilter extends Composite implements Display {

  private static SearchFilterUiBinder uiBinder = GWT.create(SearchFilterUiBinder.class);

  interface SearchFilterUiBinder extends UiBinder<Widget, SearchFilter> {
  }

  @UiField
  protected TextBox startDate;
  @UiField
  protected TextBox endDate;
  @UiField
  protected TextBox names;
  @UiField
  protected ListBox distanceListBox;
  @UiField
  protected ListBox policeStation;
  @UiField
  protected HTMLPanel mapPanel;
  @UiField
  protected RadioButton searchByPoliceStation;
  @UiField
  protected RadioButton searchByLatLng;
  @UiField
  protected HTMLPanel searchByLatLngContainer;
  @UiField
  protected HTMLPanel searchByPolStationContainer;
  @UiField
  protected Label errorMessage;

  private MapWidget map;
  private PopupPanel popupCalendarPanel;
  private DatePicker startDatePicker;
  private DatePicker endDatePicker;
  private SearchFilterPresenter presenter;
  private double latitude;
  private double longitude;

  public SearchFilter() {
    initWidget(uiBinder.createAndBindUi(this));
    initializeDateComponents();

    startDate.addFocusHandler(new FocusHandler() {

      public void onFocus(FocusEvent event) {
        displayCalendar(startDate, startDatePicker);
      }

    });
    endDate.addFocusHandler(new FocusHandler() {

      public void onFocus(FocusEvent event) {
        displayCalendar(endDate, endDatePicker);
      }

    });

    startDatePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {

      public void onValueChange(ValueChangeEvent<Date> event) {
        showSelectedDate(startDate, event.getValue());
      }

    });

    endDatePicker.addValueChangeHandler(new ValueChangeHandler<Date>() {

      public void onValueChange(ValueChangeEvent<Date> event) {
        showSelectedDate(endDate, event.getValue());
      }

    });

    initializeLocationComponents();

  }

  public void setPresenter(final SearchFilterPresenter presenter) {
    this.presenter = presenter;
  }

  public void setPoliceStationList(final List<SeguriAppEntity> policeStationList) {
    policeStation.clear();
    policeStation.addItem("Todos", "0");
    if (policeStationList != null) {
      for (final SeguriAppEntity policeStationItem : policeStationList) {
        policeStation.addItem(policeStationItem.getTitle(), policeStationItem.getId().toString());
      }
    }
  }

  @UiHandler("search")
  protected void onSearchClick(final ClickEvent event) {
    if (validateFilters()) {
      showErrorMessage(false);
      presenter.applyFilters(startDate.getText(), endDate.getText(), names.getText(), latitude, longitude,
          distanceListBox.getValue(distanceListBox.getSelectedIndex()),
          policeStation.getValue(policeStation.getSelectedIndex()), searchByPoliceStation.getValue());
    } else {
      showErrorMessage(true);
    }
  }

  @UiHandler("mapDialogLink")
  protected void onMapDialogLinkClick(final ClickEvent event) {
    // Create the dialog box
    final DialogBox dialogBox = createDialogBox();
    dialogBox.setGlassEnabled(true);
    dialogBox.setAnimationEnabled(true);
    dialogBox.center();
    dialogBox.show();
  }

  @UiHandler("clearFilters")
  protected void onClearFiltersClick(final ClickEvent event) {
    startDate.setText("");
    endDate.setText("");
    names.setText("");
    latitude = 0;
    longitude = 0;
    distanceListBox.setSelectedIndex(0);
    policeStation.setSelectedIndex(0);
    searchByPoliceStation.setValue(true);
    setSearchContainersVisibility(false);
  }

  private void initializeLocationComponents() {
    distanceListBox.clear();
    distanceListBox.addItem("5 km", "5");
    distanceListBox.addItem("10 km", "10");
    distanceListBox.addItem("15 km", "15");
    distanceListBox.addItem("20 km", "20");
    updateMapInfo();
    searchByPoliceStation.setValue(true);
    searchByLatLng.setValue(false);
    setSearchContainersVisibility(false);
    searchByPoliceStation.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
      public void onValueChange(ValueChangeEvent<Boolean> event) {
        if (event.getValue()) {
          setSearchContainersVisibility(false);
        }
      }
    });
    searchByLatLng.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
      public void onValueChange(ValueChangeEvent<Boolean> event) {
        if (event.getValue()) {
          setSearchContainersVisibility(true);
        }
      }
    });
  }

  private void setSearchContainersVisibility(final boolean searchByLatLong) {
    searchByLatLngContainer.setVisible(searchByLatLong);
    searchByPolStationContainer.setVisible(!searchByLatLong);
  }

  private boolean validateFilters() {
    if (startDate.getText().isEmpty() && endDate.getText().isEmpty() && names.getText().isEmpty()) {
      if ((searchByPoliceStation.getValue() && policeStation.getSelectedIndex() == 0)
          || searchByLatLng.getValue() && latitude == 0 && longitude == 0) {
        return false;
      }
    }
    boolean valid = true;
    if (!startDate.getText().isEmpty() && !UtilClientValidations.validateDate(startDate.getText())) {
      valid = false;
      startDate.setStyleName("dateBoxFormatError", true);
    }
    if (!endDate.getText().isEmpty() && !UtilClientValidations.validateDate(endDate.getText())) {
      valid = false;
      endDate.setStyleName("dateBoxFormatError", true);
    }
    return valid;
  }

  private void showSelectedDate(final TextBox textBox, final Date date) {
    final String dateStr = DateTimeFormat.getFormat("dd/MM/yyyy").format(date);
    textBox.setText(dateStr);
    popupCalendarPanel.hide();
  }

  private void displayCalendar(final TextBox textBox, final DatePicker datePicker) {
    if (popupCalendarPanel.isShowing()) {
      popupCalendarPanel.hide();
    }
    popupCalendarPanel.clear();
    popupCalendarPanel.add(datePicker);
    popupCalendarPanel.setPopupPosition(textBox.getAbsoluteLeft(), textBox.getAbsoluteTop());
    popupCalendarPanel.show();
  }

  private void initializeDateComponents() {
    startDatePicker = new DatePicker();
    endDatePicker = new DatePicker();
    popupCalendarPanel = new PopupPanel(true);
    popupCalendarPanel.addStyleName("calendar");
  }

  /**
   * Create the dialog box for this example.
   * 
   * @return the new dialog box
   */
  private DialogBox createDialogBox() {
    // Create map
    final LatLng latLng = LatLng.newInstance(latitude, longitude);
    final MapWidget map = new MapWidget(latLng, 13);
    // Create a dialog box and set the caption text
    final DialogBox dialogBox = new DialogBox();
    dialogBox.ensureDebugId("cwDialogBox");
    dialogBox.setText("Seleccionar ubicaci\u00F3n");
    // Create a table to layout the content
    final VerticalPanel dialogContents = new VerticalPanel();
    dialogContents.setSpacing(4);
    dialogBox.setWidget(dialogContents);

    // Add some text to the top of the dialog
    final HorizontalPanel searchWidget = new HorizontalPanel();
    final HTML details = new HTML("Ingresar lugar: ");
    final TextBox placeTextBox = new TextBox();
    final Button submitButton = new Button("Ir", new ClickHandler() {

      public void onClick(ClickEvent event) {
        if (placeTextBox.getText() != null && !placeTextBox.getText().isEmpty()) {
          final Geocoder geocoder = new Geocoder();
          geocoder.getLatLng(placeTextBox.getText(), new LatLngCallback() {

            public void onFailure() {
              Window.alert("Error buscando " + placeTextBox.getText());
            }

            public void onSuccess(final LatLng point) {
              map.setCenter(point, 13);
            }
          });
        }
      }
    });
    searchWidget.add(details);
    searchWidget.add(placeTextBox);
    searchWidget.add(submitButton);
    dialogContents.add(searchWidget);
    dialogContents.setCellHorizontalAlignment(details, HasHorizontalAlignment.ALIGN_CENTER);

    // Add the map to the dialog
    map.setSize("700px", "500px");
    map.setScrollWheelZoomEnabled(true);
    map.addControl(new SmallMapControl());
    map.addMapClickHandler(new MapClickHandler() {

      public void onClick(MapClickEvent e) {
        MapWidget sender = e.getSender();
        Overlay overlay = e.getOverlay();
        LatLng point = e.getLatLng();

        if (overlay != null && overlay instanceof Marker) {
          sender.removeOverlay(overlay);
          latitude = 0;
          longitude = 0;
        } else {
          sender.clearOverlays();
          sender.addOverlay(new Marker(point));
          latitude = point.getLatitude();
          longitude = point.getLongitude();
        }
      }
    });
    dialogContents.add(map);
    dialogContents.setCellHorizontalAlignment(map, HasHorizontalAlignment.ALIGN_CENTER);

    final Button closeButton = new Button("Cerrar", new ClickHandler() {

      public void onClick(ClickEvent event) {
        dialogBox.hide();
      }
    });
    final Button selectButton = new Button("Seleccionar", new ClickHandler() {

      public void onClick(ClickEvent event) {
        updateMapInfo();
        dialogBox.hide();
      }
    });
    dialogContents.add(selectButton);
    dialogContents.add(closeButton);
    if (LocaleInfo.getCurrentLocale().isRTL()) {
      dialogContents.setCellHorizontalAlignment(closeButton, HasHorizontalAlignment.ALIGN_LEFT);
      dialogContents.setCellHorizontalAlignment(selectButton, HasHorizontalAlignment.ALIGN_LEFT);

    } else {
      dialogContents.setCellHorizontalAlignment(closeButton, HasHorizontalAlignment.ALIGN_RIGHT);
      dialogContents.setCellHorizontalAlignment(selectButton, HasHorizontalAlignment.ALIGN_RIGHT);
    }

    // Return the dialog box
    return dialogBox;
  }

  private void updateMapInfo() {
    final MapOptions mapOptions = MapOptions.newInstance();
    mapOptions.setSize(Size.newInstance(150, 150));
    map = new MapWidget(LatLng.newInstance(latitude, longitude), 14, mapOptions);
    map.setScrollWheelZoomEnabled(true);
    map.addControl(new SmallMapControl());
    if (latitude == 0 && longitude == 0) {
      getUserLocation();
    }
    map.addOverlay(new Marker(LatLng.newInstance(latitude, longitude)));
    mapPanel.clear();
    mapPanel.add(map);
  }

  private void getUserLocation() {
    final Geolocation geolocation = Geolocation.getIfSupported();
    geolocation.getCurrentPosition(new Callback<Position, PositionError>() {

      public void onSuccess(Position result) {
        map.setCenter(LatLng.newInstance(result.getCoordinates().getLatitude(), result.getCoordinates().getLongitude()));
        latitude = result.getCoordinates().getLatitude();
        longitude = result.getCoordinates().getLongitude();
      }

      public void onFailure(PositionError reason) {
        Window.alert("error");
      }
    });
  }

  private void showErrorMessage(final boolean showFlag) {
    errorMessage.setVisible(showFlag);
  }

}
